using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Diagnostics;

namespace ComputaGuru.SpEAd
{
    public class Program
    {
        const string MenuTextRoot = "-Sp&EAd";
        const string MenuTextAbout = "&About SpEAd...";
        const string MenuTextSeperator = "-";
        const string MenuTextSetLinkStyleAll = "Set Link &Style All";
        const string MenuTextSetLinkStyle = "Set Link St&yle";
        const string MenuTextLinkToMultiple = "Link To &Multiple";
        const string MenuTextLinkMultipleTo = "&Link Multiple To";
        const string MenuTextHideLinks = "&Hide Links";
        const string MenuTextUnhideLinks = "&Unhide Links";
        const string MenuTextHideLinkLabelsAll = "Hide Lin&k Labels All";
        const string MenuTextUnhideLinkLabelsAll = "Unhide Link La&bels All";
        const string MenuTextHideLinkLabels = "Hi&de Link Labels";
        const string MenuTextUnhideLinkLabels = "Unh&ide Link Labels";
        const string MenuTextSyncOpsAndAtts = "S&ync Operations and Attributes";
        const string MenuTextCopyOpsAndAtts = "Co&py Operations and Attributes";

        #region EA Automation API methods

        /// <summary>
        /// Called by EA on loading as it connects to the Add-in
        /// </summary>
        /// <param name="Repository">A reference to the current EA repository which is effectively the entry point for automation</param>
        /// <returns></returns>
        /// <remarks>
        /// This is where any initialisation or special processing should take place
        /// </remarks>
        public string EA_Connect(EA.Repository Repository)
        {
            // Nothing to do other than return
            return String.Empty;
        }

        /// <summary>
        /// Called by EA when the user has an Add-in menu highlighted and presses F1
        /// </summary>
        /// <param name="Repository">A reference to the current EA repository</param>
        /// <param name="Location">String indicating the part of the EA UI that triggered the event</param>
        /// <param name="MenuName">Name of the parent menu where this event was triggered from</param>
        /// <param name="ItemName">Name of the menu item that was selected when the event was triggered</param>
        public void EA_ShowHelp(EA.Repository Repository, string Location, string MenuName, string ItemName)
        {
            //do nothing these features are pretty simple and self explanatory
        }

        /// <summary>
        /// Called by EA as it loads the Add-in requesting a list of menu items to display
        /// </summary>
        /// <param name="Repository">Reference to the current EA repository</param>
        /// <param name="Location"></param>
        /// <param name="MenuName">The name of the menu or sub menu being populated</param>
        /// <returns></returns>
        public object EA_GetMenuItems(EA.Repository Repository, string Location, string MenuName)
        {
            switch (MenuName)
            {
                case "":
                    return MenuTextRoot;
                case MenuTextRoot:
                    string[] ar = {MenuTextSetLinkStyleAll
                                    , MenuTextSetLinkStyle
                                    , MenuTextSeperator
                                    , MenuTextHideLinks
                                    , MenuTextUnhideLinks
                                    , MenuTextHideLinkLabelsAll
                                    , MenuTextUnhideLinkLabelsAll
                                    , MenuTextHideLinkLabels
                                    , MenuTextUnhideLinkLabels
                                    , MenuTextSeperator
                                    , MenuTextLinkToMultiple
                                    , MenuTextLinkMultipleTo
                                    , MenuTextSeperator
                                    , MenuTextSyncOpsAndAtts
                                    , MenuTextCopyOpsAndAtts
                                    , MenuTextSeperator
                                    , MenuTextAbout};
                    return ar;
            }
            return String.Empty;
        }

        bool IsProjectOpen(EA.Repository Repository)
        {
            try
            {
                EA.Collection c = Repository.Models;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Called by EA when it needs to identify the state for menu items related to the Add-in
        /// </summary>
        /// <param name="Repository">Reference to the current EA repository</param>
        /// <param name="Location">String indicating the part of the EA UI that triggered the event</param>
        /// <param name="MenuName">Name of the parent menu that is requesting the state of sub items</param>
        /// <param name="ItemName">Name of the actual item state is being requested for</param>
        /// <param name="IsEnabled">Returns state to set the item to</param>
        /// <param name="IsChecked">Returns checked status for the item</param>
        public void EA_GetMenuState(EA.Repository Repository, string Location, string MenuName, string ItemName, ref bool IsEnabled, ref bool IsChecked)
        {
            if (IsProjectOpen(Repository))
            {
                //a model/repository is open
                if (ItemName == MenuTextSetLinkStyleAll
                    || ItemName == MenuTextSetLinkStyle
                    || ItemName == MenuTextHideLinks
                    || ItemName == MenuTextLinkMultipleTo
                    || ItemName == MenuTextLinkToMultiple
                    || ItemName == MenuTextSyncOpsAndAtts
                    || ItemName == MenuTextCopyOpsAndAtts
                    || ItemName == MenuTextHideLinkLabelsAll
                    || ItemName == MenuTextUnhideLinkLabelsAll
                    || ItemName == MenuTextHideLinkLabels
                    || ItemName == MenuTextUnhideLinkLabels)
                {
                    //set default state based on whether a diagram is open
                    EA.Diagram activeDiagram = Repository.GetCurrentDiagram();
                    IsEnabled = (activeDiagram != null && !activeDiagram.IsLocked);

                    //adjust state for those items that also require a selected object
                    if (IsEnabled && (ItemName == MenuTextLinkMultipleTo
                                    || ItemName == MenuTextLinkToMultiple
                                    || ItemName == MenuTextSyncOpsAndAtts
                                    || ItemName == MenuTextCopyOpsAndAtts))
                    {

                        IsEnabled = (activeDiagram.SelectedObjects.Count == 1);
                    }
                }
            }
            else
            {
                // If no open models, disable all menu items
                IsEnabled = false;
            }
        }

        /// <summary>
        /// Called by EA when a menu item in for an add-in is clicked
        /// </summary>
        /// <param name="Repository">Reference to the current EA repository</param>
        /// <param name="Location">String indicating the part of the EA UI that triggered the event</param>
        /// <param name="MenuName">Name of the parent menu that triggered the event</param>
        /// <param name="ItemName">Actual menu item that was selected</param>
        public void EA_MenuClick(EA.Repository Repository, string Location, string MenuName, string ItemName)
        {
            if (this.ensureCleanDiagram(Repository))
            {
                switch (ItemName)
                {
                    case MenuTextSetLinkStyleAll:
                        this.setLinkStyleAll(Repository);
                        break;
                    case MenuTextSetLinkStyle:
                        this.setLinkStyle(Repository);
                        break;
                    case MenuTextLinkMultipleTo:
                        this.linkMultipleTo(Repository);
                        break;
                    case MenuTextLinkToMultiple:
                        this.linkToMultiple(Repository);
                        break;
                    case MenuTextHideLinks:
                        this.hideLinks(Repository);
                        break;
                    case MenuTextUnhideLinks:
                        this.unhideLinks(Repository);
                        break;
                    case MenuTextHideLinkLabelsAll:
                        this.hideLinkLabelsAll(Repository);
                        break;
                    case MenuTextUnhideLinkLabelsAll:
                        this.unhideLinkLabelsAll(Repository);
                        break;
                    case MenuTextHideLinkLabels:
                        this.hideLinkLabels(Repository);
                        break;
                    case MenuTextUnhideLinkLabels:
                        this.unhideLinkLabels(Repository);
                        break;
                    case MenuTextSyncOpsAndAtts:
                        this.syncOpsAndAtts(Repository);
                        break;
                    case MenuTextCopyOpsAndAtts:
                        this.copyOpsAndAtts(Repository);
                        break;
                    case MenuTextAbout:
                        this.showAbout();
                        break;
                    default:
                        break;
                }
            }
        }

        public void EA_Disconnect()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }


        #endregion

        /// <summary>
        /// Display the about dialog
        /// </summary>
        private void showAbout()
        {
            using (About aboutForm = new About())
            {
                aboutForm.ShowDialog();
            }
        }

        /// <summary>
        /// Copy Attributes and/or Operations from the active element to selected elements
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void copyOpsAndAtts(EA.Repository repository)
        {
            using (AttributeOperatorSelector attOppSelector = new AttributeOperatorSelector())
            {
                //get the active diagram and element
                EA.Diagram activeDiagram = repository.GetCurrentDiagram();
                EA.DiagramObject activeObject = (EA.DiagramObject)activeDiagram.SelectedObjects.GetAt(0);
                EA.Element activeElement = repository.GetElementByID(activeObject.ElementID);

                attOppSelector.ActiveElement = activeElement;

                if (attOppSelector.ShowDialog() == DialogResult.OK)
                {
                    using (ElementSelector elementSelector = new ElementSelector())
                    {
                        //get target element selections from user
                        elementSelector.EaRepository = repository;
                        elementSelector.Mode = ElementSelectorMode.CopyAttsAndOps;
                        elementSelector.SelectionMode = SelectionMode.MultiExtended;

                        if (elementSelector.ShowDialog() == DialogResult.OK)
                        {
                            foreach (ElementDescriptor targetElement in elementSelector.SelectedElements)
                            {
                                if (attOppSelector.SelectedAttributes.Count != 0)
                                {
                                    EA.Attribute newAttribute = null;
                                    foreach (AttributeDescriptor currentAttribute in attOppSelector.SelectedAttributes)
                                    {
                                        //add the attribute from the target
                                        //and copy across properties
                                        newAttribute = (EA.Attribute)targetElement.Element.Attributes.AddNew(currentAttribute.Name, currentAttribute.AttributeType);
                                        newAttribute.Containment = currentAttribute.Attribute.Containment;
                                        newAttribute.Default = currentAttribute.Attribute.Default;
                                        newAttribute.AllowDuplicates = currentAttribute.Attribute.AllowDuplicates;
                                        newAttribute.IsCollection = currentAttribute.Attribute.IsCollection;
                                        newAttribute.IsConst = currentAttribute.Attribute.IsConst;
                                        newAttribute.IsDerived = currentAttribute.Attribute.IsDerived;
                                        newAttribute.IsOrdered = currentAttribute.Attribute.IsOrdered;
                                        newAttribute.IsStatic = currentAttribute.Attribute.IsStatic;
                                        newAttribute.Length = currentAttribute.Attribute.Length;
                                        newAttribute.LowerBound = currentAttribute.Attribute.LowerBound;
                                        newAttribute.Notes = currentAttribute.Attribute.Notes;
                                        newAttribute.Pos = currentAttribute.Attribute.Pos;
                                        newAttribute.Precision = currentAttribute.Attribute.Precision;
                                        newAttribute.Scale = currentAttribute.Attribute.Scale;
                                        newAttribute.Stereotype = currentAttribute.Attribute.Stereotype;
                                        newAttribute.Style = currentAttribute.Attribute.Style;
                                        newAttribute.Type = currentAttribute.Attribute.Type;
                                        newAttribute.UpperBound = currentAttribute.Attribute.UpperBound;
                                        newAttribute.Visibility = currentAttribute.Attribute.Visibility;

                                        newAttribute.Update();

                                        //add any contraints to the new attribute
                                        EA.Constraint newConstraint = null;
                                        foreach (EA.Constraint currentConstraint in currentAttribute.Attribute.Constraints)
                                        {
                                            newConstraint = (EA.Constraint)newAttribute.Constraints.AddNew(currentConstraint.Name, currentConstraint.Type);
                                            newConstraint.Notes = currentConstraint.Notes;
                                            newConstraint.Status = currentConstraint.Status;
                                            newConstraint.Weight = currentConstraint.Weight;
                                            newConstraint.Update();
                                        }

                                        //add any tagged values to the new attribute
                                        EA.TaggedValue newTag = null;
                                        foreach (EA.TaggedValue currentTag in currentAttribute.Attribute.TaggedValues)
                                        {
                                            newTag = (EA.TaggedValue)newAttribute.TaggedValues.AddNew(currentTag.Name, String.Empty);
                                            newTag.Notes = currentTag.Notes;
                                            newTag.Value = currentTag.Value;
                                            newTag.Update();
                                        }

                                        newAttribute.Update();
                                    }

                                    targetElement.Element.Update();
                                }



                                if (attOppSelector.SelectedOperations.Count != 0)
                                {

                                    EA.Method newMethod = null;
                                    foreach (OperationDescriptor currentMethod in attOppSelector.SelectedOperations)
                                    {
                                        //add the attribute from the target
                                        //and copy across properties
                                        newMethod = (EA.Method)targetElement.Element.Methods.AddNew(currentMethod.Name, currentMethod.OperationType);
                                        newMethod.Abstract = currentMethod.Operation.Abstract;
                                        newMethod.Behavior = currentMethod.Operation.Behavior;
                                        newMethod.Behaviour = currentMethod.Operation.Behaviour;
                                        newMethod.Code = currentMethod.Operation.Code;
                                        newMethod.Concurrency = currentMethod.Operation.Concurrency;
                                        newMethod.IsConst = currentMethod.Operation.IsConst;
                                        newMethod.IsLeaf = currentMethod.Operation.IsLeaf;
                                        newMethod.IsPure = currentMethod.Operation.IsPure;
                                        newMethod.IsQuery = currentMethod.Operation.IsQuery;
                                        newMethod.IsRoot = currentMethod.Operation.IsRoot;
                                        newMethod.IsStatic = currentMethod.Operation.IsStatic;
                                        newMethod.IsSynchronized = currentMethod.Operation.IsSynchronized;
                                        newMethod.Notes = currentMethod.Operation.Notes;
                                        newMethod.Pos = currentMethod.Operation.Pos;
                                        newMethod.ReturnIsArray = currentMethod.Operation.ReturnIsArray;
                                        newMethod.StateFlags = currentMethod.Operation.StateFlags;
                                        newMethod.Stereotype = currentMethod.Operation.Stereotype;
                                        newMethod.Style = currentMethod.Operation.Style;
                                        newMethod.Throws = currentMethod.Operation.Throws;
                                        newMethod.Visibility = currentMethod.Operation.Visibility;

                                        newMethod.Update();

                                        //add any parameters to the new method
                                        EA.Parameter newParameter = null;
                                        foreach (EA.Parameter currentParameter in currentMethod.Operation.Parameters)
                                        {
                                            newParameter = (EA.Parameter)newMethod.Parameters.AddNew(currentParameter.Name, currentParameter.Type);
                                            newParameter.Alias = currentParameter.Alias;
                                            newParameter.Default = currentParameter.Default;
                                            newParameter.IsConst = currentParameter.IsConst;
                                            newParameter.Kind = currentParameter.Kind;
                                            newParameter.Notes = currentParameter.Notes;
                                            newParameter.Position = currentParameter.Position;
                                            newParameter.Stereotype = currentParameter.Stereotype;
                                            newParameter.Style = currentParameter.Style;

                                            newParameter.Update();
                                        }

                                        //add any tagged values to the new method
                                        EA.MethodTag newTag = null;
                                        foreach (EA.MethodTag currentTag in currentMethod.Operation.TaggedValues)
                                        {
                                            newTag = (EA.MethodTag)newMethod.TaggedValues.AddNew(currentTag.Name, String.Empty);
                                            newTag.Notes = currentTag.Notes;
                                            newTag.Value = currentTag.Value;
                                            newTag.Update();
                                        }

                                        //add any preconditions to the new method
                                        EA.MethodConstraint newCondition = null;
                                        foreach (EA.MethodConstraint currentCondition in currentMethod.Operation.PreConditions)
                                        {
                                            newCondition = (EA.MethodConstraint)newMethod.PreConditions.AddNew(currentCondition.Name, currentCondition.Type);
                                            newCondition.Notes = currentCondition.Notes;

                                            newCondition.Update();
                                        }

                                        //add any postconditions to the new method
                                        foreach (EA.MethodConstraint currentCondition in currentMethod.Operation.PostConditions)
                                        {
                                            newCondition = (EA.MethodConstraint)newMethod.PostConditions.AddNew(currentCondition.Name, currentCondition.Type);
                                            newCondition.Notes = currentCondition.Notes;

                                            newCondition.Update();
                                        }

                                        newMethod.Update();
                                    }
                                    targetElement.Element.Update();
                                }

                                //save changes and reload the document
                                activeDiagram.Update();
                            }

                            repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Synchronise the active element with a selected element
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void syncOpsAndAtts(EA.Repository repository)
        {
            using (ElementSelector elementSelector = new ElementSelector())
            {
                //get target element selections from user
                elementSelector.EaRepository = repository;
                elementSelector.Mode = ElementSelectorMode.SyncElements;
                elementSelector.SelectionMode = SelectionMode.One;

                if (elementSelector.ShowDialog() == DialogResult.OK)
                {
                    //get the active diagram and element
                    EA.Diagram activeDiagram = repository.GetCurrentDiagram();
                    EA.DiagramObject activeObject = (EA.DiagramObject)activeDiagram.SelectedObjects.GetAt(0);
                    EA.Element activeElement = repository.GetElementByID(activeObject.ElementID);

                    //get the element selected by the user
                    EA.Element selectedElement = repository.GetElementByID(elementSelector.SelectedElements[0].ElementID);

                    if (activeElement != null && selectedElement != null)
                    {
                        //clear and repopulate the attributes on the active element
                        this.clearCollection(activeElement.Attributes);

                        activeElement.Update();

                        EA.Attribute newAttribute = null;
                        foreach (EA.Attribute currentAttribute in selectedElement.Attributes)
                        {
                            //add the attribute from the target
                            //and copy across properties
                            newAttribute = (EA.Attribute)activeElement.Attributes.AddNew(currentAttribute.Name, currentAttribute.Type);
                            newAttribute.Containment = currentAttribute.Containment;
                            newAttribute.Default = currentAttribute.Default;
                            newAttribute.AllowDuplicates = currentAttribute.AllowDuplicates;
                            newAttribute.IsCollection = currentAttribute.IsCollection;
                            newAttribute.IsConst = currentAttribute.IsConst;
                            newAttribute.IsDerived = currentAttribute.IsDerived;
                            newAttribute.IsOrdered = currentAttribute.IsOrdered;
                            newAttribute.IsStatic = currentAttribute.IsStatic;
                            newAttribute.Length = currentAttribute.Length;
                            newAttribute.LowerBound = currentAttribute.LowerBound;
                            newAttribute.Notes = currentAttribute.Notes;
                            newAttribute.Pos = currentAttribute.Pos;
                            newAttribute.Precision = currentAttribute.Precision;
                            newAttribute.Scale = currentAttribute.Scale;
                            newAttribute.Stereotype = currentAttribute.Stereotype;
                            newAttribute.Style = currentAttribute.Style;
                            newAttribute.Type = currentAttribute.Type;
                            newAttribute.UpperBound = currentAttribute.UpperBound;
                            newAttribute.Visibility = currentAttribute.Visibility;

                            newAttribute.Update();

                            //add any contraints to the new attribute
                            EA.Constraint newConstraint = null;
                            foreach (EA.Constraint currentConstraint in currentAttribute.Constraints)
                            {
                                newConstraint = (EA.Constraint)newAttribute.Constraints.AddNew(currentConstraint.Name, currentConstraint.Type);
                                newConstraint.Notes = currentConstraint.Notes;
                                newConstraint.Status = currentConstraint.Status;
                                newConstraint.Weight = currentConstraint.Weight;
                                newConstraint.Update();
                            }

                            //add any tagged values to the new attribute
                            EA.TaggedValue newTag = null;
                            foreach (EA.TaggedValue currentTag in currentAttribute.TaggedValues)
                            {
                                newTag = (EA.TaggedValue)newAttribute.TaggedValues.AddNew(currentTag.Name, String.Empty);
                                newTag.Notes = currentTag.Notes;
                                newTag.Value = currentTag.Value;
                                newTag.Update();
                            }

                            newAttribute.Update();
                        }

                        //clear and repopulate the methods on the active element
                        this.clearCollection(activeElement.Methods);

                        activeElement.Update();

                        EA.Method newMethod = null;
                        foreach (EA.Method currentMethod in selectedElement.Methods)
                        {
                            //add the attribute from the target
                            //and copy across properties
                            newMethod = (EA.Method)activeElement.Methods.AddNew(currentMethod.Name, currentMethod.ReturnType);
                            newMethod.Abstract = currentMethod.Abstract;
                            newMethod.Behavior = currentMethod.Behavior;
                            newMethod.Behaviour = currentMethod.Behaviour;
                            newMethod.Code = currentMethod.Code;
                            newMethod.Concurrency = currentMethod.Concurrency;
                            newMethod.IsConst = currentMethod.IsConst;
                            newMethod.IsLeaf = currentMethod.IsLeaf;
                            newMethod.IsPure = currentMethod.IsPure;
                            newMethod.IsQuery = currentMethod.IsQuery;
                            newMethod.IsRoot = currentMethod.IsRoot;
                            newMethod.IsStatic = currentMethod.IsStatic;
                            newMethod.IsSynchronized = currentMethod.IsSynchronized;
                            newMethod.Notes = currentMethod.Notes;
                            newMethod.Pos = currentMethod.Pos;
                            newMethod.ReturnIsArray = currentMethod.ReturnIsArray;
                            newMethod.StateFlags = currentMethod.StateFlags;
                            newMethod.Stereotype = currentMethod.Stereotype;
                            newMethod.Style = currentMethod.Style;
                            newMethod.Throws = currentMethod.Throws;
                            newMethod.Visibility = currentMethod.Visibility;

                            newMethod.Update();

                            //add any parameters to the new method
                            EA.Parameter newParameter = null;
                            foreach (EA.Parameter currentParameter in currentMethod.Parameters)
                            {
                                newParameter = (EA.Parameter)newMethod.Parameters.AddNew(currentParameter.Name, currentParameter.Type);
                                newParameter.Alias = currentParameter.Alias;
                                newParameter.Default = currentParameter.Default;
                                newParameter.IsConst = currentParameter.IsConst;
                                newParameter.Kind = currentParameter.Kind;
                                newParameter.Notes = currentParameter.Notes;
                                newParameter.Position = currentParameter.Position;
                                newParameter.Stereotype = currentParameter.Stereotype;
                                newParameter.Style = currentParameter.Style;

                                newParameter.Update();
                            }

                            //add any tagged values to the new method
                            EA.MethodTag newTag = null;
                            foreach (EA.MethodTag currentTag in currentMethod.TaggedValues)
                            {
                                newTag = (EA.MethodTag)newMethod.TaggedValues.AddNew(currentTag.Name, String.Empty);
                                newTag.Notes = currentTag.Notes;
                                newTag.Value = currentTag.Value;
                                newTag.Update();
                            }

                            //add any preconditions to the new method
                            EA.MethodConstraint newCondition = null;
                            foreach (EA.MethodConstraint currentCondition in currentMethod.PreConditions)
                            {
                                newCondition = (EA.MethodConstraint)newMethod.PreConditions.AddNew(currentCondition.Name, currentCondition.Type);
                                newCondition.Notes = currentCondition.Notes;

                                newCondition.Update();
                            }

                            //add any postconditions to the new method
                            foreach (EA.MethodConstraint currentCondition in currentMethod.PostConditions)
                            {
                                newCondition = (EA.MethodConstraint)newMethod.PostConditions.AddNew(currentCondition.Name, currentCondition.Type);
                                newCondition.Notes = currentCondition.Notes;

                                newCondition.Update();
                            }

                            newMethod.Update();
                        }

                        //save changes and reload the document
                        activeElement.Update();
                        activeDiagram.Update();

                        repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                    }
                }
            }
        }

        /// <summary>
        /// Hide selected links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void hideLinks(EA.Repository repository)
        {
            using (LinkSelector linkSelector = new LinkSelector())
            {
                //get target link selections from user
                linkSelector.EaRepository = repository;
                linkSelector.Mode = LinkSelectorMode.Hide;
                if (linkSelector.ShowDialog() == DialogResult.OK)
                {
                    //hide all the selected links
                    foreach (LinkDescriptor currentLink in linkSelector.SelectedLinks)
                    {
                        currentLink.Link.IsHidden = true;
                        currentLink.Link.Update();
                    }

                    repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                }
            }
        }

        /// <summary>
        /// Unhide selected links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void unhideLinks(EA.Repository repository)
        {
            using (LinkSelector linkSelector = new LinkSelector())
            {
                //get target link selections from user
                linkSelector.EaRepository = repository;
                linkSelector.Mode = LinkSelectorMode.Unhide;
                if (linkSelector.ShowDialog() == DialogResult.OK)
                {
                    //hide all the selected links
                    foreach (LinkDescriptor currentLink in linkSelector.SelectedLinks)
                    {
                        currentLink.Link.IsHidden = false;
                        currentLink.Link.Update();
                    }

                    repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                }
            }
        }

        /// <summary>
        /// Hide all labels on selected links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void hideLinkLabelsAll(EA.Repository repository)
        {
            using (LinkSelector linkSelector = new LinkSelector())
            {
                //get target link selections from user
                linkSelector.EaRepository = repository;
                linkSelector.Mode = LinkSelectorMode.Hide;
                if (linkSelector.ShowDialog() == DialogResult.OK)
                {
                    //hide all the selected links
                    DiagramLinkGeometry linkGeometry = null;
                    foreach (LinkDescriptor currentLink in linkSelector.SelectedLinks)
                    {
                        linkGeometry = new DiagramLinkGeometry(currentLink.Link.Geometry);

                        foreach (DiagramLinkLabel linkLabel in linkGeometry.Labels)
                        {
                            linkLabel.HDN = "1";
                        }

                        currentLink.Link.Geometry = linkGeometry.ToString();
                        currentLink.Link.Update();
                    }

                    repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                }
            }
        }

        /// <summary>
        /// Unhide all hidden labels on selected links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void unhideLinkLabelsAll(EA.Repository repository)
        {
            using (LinkSelector linkSelector = new LinkSelector())
            {
                //get target link selections from user
                linkSelector.EaRepository = repository;
                linkSelector.Mode = LinkSelectorMode.Hide;
                if (linkSelector.ShowDialog() == DialogResult.OK)
                {
                    //hide all the selected links
                    DiagramLinkGeometry linkGeometry = null;
                    foreach (LinkDescriptor currentLink in linkSelector.SelectedLinks)
                    {
                        linkGeometry = new DiagramLinkGeometry(currentLink.Link.Geometry);

                        foreach (DiagramLinkLabel linkLabel in linkGeometry.Labels)
                        {
                            if (linkLabel.HDN == "1")
                            {
                                linkLabel.HDN = "0";
                            }
                        }

                        currentLink.Link.Geometry = linkGeometry.ToString();
                        currentLink.Link.Update();
                    }

                    repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                }
            }
        }

        /// <summary>
        /// Hide selected labels on selected links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void hideLinkLabels(EA.Repository repository)
        {
            using (LabelSelector labelSelector = new LabelSelector())
            {
                if (labelSelector.ShowDialog() == DialogResult.OK)
                {
                    using (LinkSelector linkSelector = new LinkSelector())
                    {
                        //get target link selections from user
                        linkSelector.EaRepository = repository;
                        linkSelector.Mode = LinkSelectorMode.Hide;
                        if (linkSelector.ShowDialog() == DialogResult.OK)
                        {
                            //hide all the selected links
                            DiagramLinkGeometry linkGeometry = null;
                            foreach (LinkDescriptor currentLink in linkSelector.SelectedLinks)
                            {
                                linkGeometry = new DiagramLinkGeometry(currentLink.Link.Geometry);

                                foreach (DiagramLinkLabel linkLabel in linkGeometry.Labels)
                                {
                                    foreach (LinkLabelState checkedLabel in labelSelector.CheckedItems)
                                    {
                                        if (linkLabel.LabelName == checkedLabel.Symbol)
                                        {
                                            linkLabel.HDN = "1";
                                        }
                                    }
                                }

                                currentLink.Link.Geometry = linkGeometry.ToString();
                                currentLink.Link.Update();
                            }

                            repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Unhide select hidden labels on selected links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void unhideLinkLabels(EA.Repository repository)
        {
            using (LabelSelector labelSelector = new LabelSelector())
            {
                if (labelSelector.ShowDialog() == DialogResult.OK)
                {
                    using (LinkSelector linkSelector = new LinkSelector())
                    {
                        //get target link selections from user
                        linkSelector.EaRepository = repository;
                        linkSelector.Mode = LinkSelectorMode.Hide;
                        if (linkSelector.ShowDialog() == DialogResult.OK)
                        {
                            //hide all the selected links
                            DiagramLinkGeometry linkGeometry = null;
                            foreach (LinkDescriptor currentLink in linkSelector.SelectedLinks)
                            {
                                linkGeometry = new DiagramLinkGeometry(currentLink.Link.Geometry);

                                foreach (DiagramLinkLabel linkLabel in linkGeometry.Labels)
                                {
                                    foreach (LinkLabelState checkedLabel in labelSelector.CheckedItems)
                                    {
                                        if (linkLabel.LabelName == checkedLabel.Symbol)
                                        {
                                            linkLabel.HDN = "0";
                                        }
                                    }
                                }

                                currentLink.Link.Geometry = linkGeometry.ToString();
                                currentLink.Link.Update();
                            }

                            repository.ReloadDiagram(repository.GetCurrentDiagram().DiagramID);
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Link the active element to multiple elements
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void linkToMultiple(EA.Repository repository)
        {

            using (ElementSelector elementSelector = new ElementSelector())
            {
                //get target element selections from user
                elementSelector.EaRepository = repository;
                if (elementSelector.ShowDialog() == DialogResult.OK)
                {
                    using (LinkTypeSelector typeSelector = new LinkTypeSelector())
                    {
                        string linkType = elementSelector.SelectedLinkType.ToString();

                        //get references to active diagram and element
                        EA.Diagram activeDiagram = repository.GetCurrentDiagram();
                        EA.DiagramObject activeObject = (EA.DiagramObject)activeDiagram.SelectedObjects.GetAt(0);
                        EA.Element activeElement = repository.GetElementByID(activeObject.ElementID);

                        //add a link from the active element
                        //to each of the selected target elements
                        foreach (ElementDescriptor sourceElement in elementSelector.SelectedElements)
                        {
                            //add the connector to the model first
                            EA.Connector newConnector = (EA.Connector)activeElement.Connectors.AddNew(String.Empty, linkType);
                            newConnector.SupplierID = sourceElement.Element.ElementID;
                            newConnector.Update();
                            activeElement.Refresh();

                            //add link to the diagram links for the connector
                            EA.DiagramLink newLink = (EA.DiagramLink)activeDiagram.DiagramLinks.AddNew(String.Empty, linkType);
                            newLink.ConnectorID = newConnector.ConnectorID;
                            newLink.Update();
                        }

                        //save changes to the diagram and reload it
                        //if you don't reload it changes are not visible
                        activeDiagram.Update();
                        repository.ReloadDiagram(activeDiagram.DiagramID);
                    }
                }
            }
        }

        /// <summary>
        /// Link multiple selected items to the active element
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void linkMultipleTo(EA.Repository repository)
        {
            using (ElementSelector elementSelector = new ElementSelector())
            {
                //get target element selections from user
                elementSelector.EaRepository = repository;
                if (elementSelector.ShowDialog() == DialogResult.OK)
                {
                    using (LinkTypeSelector typeSelector = new LinkTypeSelector())
                    {
                        string linkType = elementSelector.SelectedLinkType.ToString();

                        //get references to active diagram and element
                        EA.Diagram activeDiagram = repository.GetCurrentDiagram();
                        EA.DiagramObject activeElement = (EA.DiagramObject)activeDiagram.SelectedObjects.GetAt(0);

                        //add a link to the active element
                        //from each of the selected elements
                        foreach (ElementDescriptor sourceElement in elementSelector.SelectedElements)
                        {
                            //add the connector to the model first
                            EA.Connector newConnector = (EA.Connector)sourceElement.Element.Connectors.AddNew(String.Empty, linkType);
                            newConnector.SupplierID = activeElement.ElementID;
                            newConnector.Update();
                            sourceElement.Element.Refresh();

                            //add link to the diagram links for the connector
                            EA.DiagramLink newLink = (EA.DiagramLink)activeDiagram.DiagramLinks.AddNew(String.Empty, linkType);
                            newLink.ConnectorID = newConnector.ConnectorID;
                            newLink.Update();
                        }

                        //save changes to the diagram and reload it
                        //if you don't reload it changes are not visible
                        activeDiagram.Update();
                        repository.ReloadDiagram(activeDiagram.DiagramID);
                    }
                }
            }
        }

        /// <summary>
        /// Set the style of all links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void setLinkStyleAll(EA.Repository repository)
        {
            using (LinkStyleSelector styleSelector = new LinkStyleSelector())
            {
                if (styleSelector.ShowDialog() == DialogResult.OK)
                {
                    //a style has been selected and the user has not cancelled
                    EA.Diagram activeDiagram = repository.GetCurrentDiagram();

                    //convert selections into string values to
                    //be inserted into style string
                    string styleMode = this.getStyleMode(styleSelector.SelectedLinkStyle);
                    string treeMode = this.getTreeMode(styleSelector.SelectedLinkStyle);

                    //set the style for each link in the diagram
                    foreach (EA.DiagramLink currentItem in activeDiagram.DiagramLinks)
                    {
                        if (!currentItem.IsHidden)
                        {
                            currentItem.Style = this.getStyleString(currentItem.Style, styleMode, treeMode);
                            currentItem.Update();
                        }
                    }

                    //save changes to the diagram and reload it
                    //if you don't reload it changes are not visible
                    bool updatedDiagram = activeDiagram.Update();
                    repository.ReloadDiagram(activeDiagram.DiagramID);
                }
            }
        }

        /// <summary>
        /// Set the style of selected links in the current diagram
        /// </summary>
        /// <param name="repository">Current ea repository</param>
        private void setLinkStyle(EA.Repository repository)
        {
            using (LinkSelector linkSelector = new LinkSelector())
            {
                linkSelector.Mode = LinkSelectorMode.SetStyle;
                linkSelector.EaRepository = repository;
                if (linkSelector.ShowDialog() == DialogResult.OK)
                {
                    //a style has been selected and the user has not cancelled
                    EA.Diagram activeDiagram = repository.GetCurrentDiagram();

                    //convert selections into string values to
                    //be inserted into style string
                    string styleMode = this.getStyleMode(linkSelector.SelectedStyle);
                    string treeMode = this.getTreeMode(linkSelector.SelectedStyle);

                    //set the style for each link in the diagram
                    foreach (LinkDescriptor currentItem in linkSelector.SelectedLinks)
                    {
                        currentItem.Link.Style = this.getStyleString(currentItem.Link.Style, styleMode, treeMode);
                        currentItem.Link.Update();
                    }

                    //save changes to the diagram and reload it
                    //if you don't reload it changes are not visible
                    bool updatedDiagram = activeDiagram.Update();
                    repository.ReloadDiagram(activeDiagram.DiagramID);
                }
            }
        }

        /// <summary>
        /// Helper method to build the style string for a connector
        /// </summary>
        /// <param name="styleString">Original style string</param>
        /// <param name="styleMode">Value for the Mode element of the style string</param>
        /// <param name="treeMode">Value for the TREE element of the style string</param>
        /// <returns></returns>
        private string getStyleString(string styleString, string styleMode, string treeMode)
        {
            //break the original style string into individual key/value pairs
            string[] styleItems = styleString.Split(';');

            //initialise the string with the mode setting
            string returnString = "Mode=" + styleMode + ";";

            //add style elements from original style
            //except Mode and TREE
            foreach (string styleItem in styleItems)
            {
                if (!String.IsNullOrEmpty(styleItem))
                {
                    if (!styleItem.StartsWith("Mode") && !styleItem.StartsWith("TREE"))
                    {
                        returnString += styleItem + ";";
                    }
                }
            }

            //add the TREE setting if appropriate
            if (!String.IsNullOrEmpty(treeMode))
            {
                returnString += "TREE=" + treeMode;
            }
            return returnString;
        }

        /// <summary>
        /// Helper method to convert a link style selection into an appropriate value for the Mode setting of a style string
        /// </summary>
        /// <param name="targetStyle">Selected Link Style</param>
        /// <returns>Numeric Mode value as a string</returns>
        private string getStyleMode(LinkStyle targetStyle)
        {
            switch (targetStyle)
            {
                case LinkStyle.Direct:
                    return "1";
                case LinkStyle.AutoRouted:
                    return "2";
                default:
                    return "3";
            }
        }

        /// <summary>
        /// Helper method to convert a link style selection into an appropriate value for the TREE setting in a style string
        /// </summary>
        /// <param name="targetStyle">Selected Link Style</param>
        /// <returns>Appropriate Tree mode setting or empty string if no tree mode selected</returns>
        private string getTreeMode(LinkStyle targetStyle)
        {
            switch (targetStyle)
            {
                case LinkStyle.TreeHorizontal:
                    return "H";
                case LinkStyle.TreeVertical:
                    return "V";
                default:
                    return String.Empty;
            }
        }

        /// <summary>
        /// Helper method to ensure the user saves changes to the current diagram or cancels the operation
        /// </summary>
        /// <param name="repository">Current EA reposiitory</param>
        /// <returns>True if the diagram is saved, false otherwise</returns>
        private bool ensureCleanDiagram(EA.Repository repository)
        {
            //default to dirty diagram
            bool isCleanDiagram = false;

            //get reference to the current diagram
            EA.Diagram currentDiagram = repository.GetCurrentDiagram();

            if (repository.IsTabOpen("*" + currentDiagram.Name) != 0)
            {
                //EA indicates a diagram is dirty by prefixing the tab name with an *
                //in most cases if a tab is open that matches the name of the diagram
                //prefixed with an * there is a strong likelihood that the diagram has
                //unsaved changes. If the model contains diagrams with duplicate names
                //it is possible that the current diagram is not dirty, but there is no
                //harm in prompting them to save anyway.

                if (MessageBox.Show("The current diagram appears to have changes that have not been saved, EA Utilities requires the diagram to be saved.\n"
                                + "Do you want to save changes to the current diagram?."
                            , "Diagram Changes"
                            , MessageBoxButtons.YesNo
                            , MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    repository.SaveDiagram(currentDiagram.DiagramID);
                    isCleanDiagram = true;
                }
            }
            else
            {
                isCleanDiagram = true;
            }

            return isCleanDiagram;
        }

        /// <summary>
        /// Helper method to clear an EA.Collection
        /// </summary>
        /// <param name="targetCollection">Collection to be cleared</param>
        private void clearCollection(EA.Collection targetCollection)
        {
            short itemIndex = Convert.ToInt16(targetCollection.Count);
            while (targetCollection.Count != 0)
            {
                itemIndex--;
                targetCollection.Delete(itemIndex);
                targetCollection.Refresh();
            }
        }
    }
}